#include "recv_helper.h"

#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "cmd.h"
#include "common.h"
#include "packet_base.h"
#include "log.h"
#include "game_map_mgr.h"
#include "app.h"
#include "player.h"


#include "attributes/common.pb.h"
#include "attributes/player.pb.h"
#include "attributes/scene.pb.h"
#include "attributes/combat.pb.h"

#include "service/account_service.pb.h"
#include "service/common_service.pb.h"
#include "service/scene_service.pb.h"
#include "service/player_service.pb.h"



RecvHelper::RecvHelper(/* args */)
{
}

RecvHelper::~RecvHelper()
{
}

void RecvHelper::Init(int console_fd, int socket_fd){
    console_fd_ = console_fd;
    socket_fd_ = socket_fd;

    cmd2func_.emplace(cmd::SERVER_COMMON_RESPONSE, std::bind(&RecvHelper::CommonRet, this, std::placeholders::_1, std::placeholders::_2));
    cmd2func_.emplace(cmd::SERVER_LOGIN_RESPONSE, std::bind(&RecvHelper::LoginRet, this, std::placeholders::_1, std::placeholders::_2));
    cmd2func_.emplace(cmd::SERVER_PLAYER_INFO_RESPONSE, std::bind(&RecvHelper::PlayerInfoRet, this, std::placeholders::_1, std::placeholders::_2));
    cmd2func_.emplace(cmd::SERVER_ENTER_DEFAULT_SCENE_RESPONSE, std::bind(&RecvHelper::EnterDefaultSceneResponse, this, std::placeholders::_1, std::placeholders::_2));
    // cmd2func_.emplace(cmd::SERVER_ENTER, std::bind(&RecvHelper::EnterSceneRet, this, std::placeholders::_1, std::placeholders::_2));
    // cmd2func_.emplace(cmd::SERVER_LEAVE, std::bind(&RecvHelper::LeaveSceneRet, this, std::placeholders::_1, std::placeholders::_2));
    cmd2func_.emplace(cmd::SERVER_MOVE_RESPONSE, std::bind(&RecvHelper::MoveRet, this, std::placeholders::_1, std::placeholders::_2));
    cmd2func_.emplace(cmd::SERVER_USE_SKILL_RESPONSE, std::bind(&RecvHelper::SkillRet, this, std::placeholders::_1, std::placeholders::_2));
}

void RecvHelper::PrintConsole(const std::string& msg){
    int ret = ::send(console_fd_, msg.c_str(), msg.length(), 0);
    if(ret <= 0){
        LOG_ERR("远程控制台打印失败，打印在当前控制台");
        LOG_DEBUG(msg);
    }
}

int RecvHelper::Recv(){
    memset(recvbuff_, 0, common::MAXLINE);
    int ret = ::recv(socket_fd_, recvbuff_, common::HEAD_LEN, 0);
    if(ret > 0){
        memcpy(&head_, recvbuff_, common::HEAD_LEN);
        head_ = ntohs(head_);
        // LOG_DEBUG("head len:{}", head_);
        PrintConsole(std::to_string(head_));

        ret = ::recv(socket_fd_, recvbuff_ + 2, head_, 0);
        if(ret > 0){
            PacketBase pack;
            pack.Copy(recvbuff_, head_ + 4);

            int cmd = pack.GetCmd();
            std::string body(pack.GetOriginBuffer() + 4, head_ - 2);

            std::string pbType = cmd::GetPbType(cmd);
            // LOG_DEBUG("cmd_id:{}, pbType:{}", cmd, pbType);
            
            Process(cmd, body);

            memset(recvbuff_, 0, common::MAXLINE);
        }else{
            LOG_ERR("exit recv1");
            return -1;
        }

    }else{
        LOG_ERR("exit recv2");
        return -1;
    }

    memset(recvbuff_, 0, common::MAXLINE);
    return ret;
}

bool RecvHelper::CheckCmd(int cmd_id){
    return cmd2func_.find(cmd_id) != cmd2func_.end();
}

void RecvHelper::Process(int cmd_id, std::string& buff){
    if(!CheckCmd(cmd_id)){
        LOG_ERR("recv消息没有注册此类型cmd_id：{}", cmd_id);
        return;
    }

    auto func = cmd2func_[cmd_id];

    std::string pbTy = cmd::GetPbType(cmd_id);
    func(pbTy, buff);
}

void RecvHelper::CommonRet(std::string& pbTy, std::string& buff){
    service::common::CommonResponse response;
    response.ParseFromString(buff);
    PrintConsole(response.Utf8DebugString());
}

void RecvHelper::LoginRet(std::string& pbTy, std::string& buff){
    service::account::LoginRequest::Response response;
    response.ParseFromString(buff);
    PrintConsole(response.Utf8DebugString());
    int error_code = response.error_code();
    if(error_code == attributes::common::ErrorCode::SUCCESS){
        App::GetIns().InitMainPlayer(response.player_base_info().uid(), socket_fd_);
    }
}

void RecvHelper::PlayerInfoRet(std::string& pbTy, std::string& buff){
    service::player::GetPlayerBaseInfoRequest::Response response;
    // player::PlayerInfo::Ret player_info_ret;
    response.ParseFromString(buff);
    PrintConsole(response.Utf8DebugString());

    // scene::SceneInfo scene_info = player_info_ret.scene_info();

    // player::FightInfo pf = player_info_ret.fight_info();
    // FightInfo fight_info;
    // fight_info.Init(pf.atk(), pf.def(), pf.max_hp(), pf.cur_hp(), pf.max_mp(), pf.cur_mp(), pf.speed(), pf.critical_rate(), pf.critical_damage(), pf.evasion_rate(), pf.hit_rate());
    // App::GetIns().GetMainPlayer()->SetFightInfo(fight_info);
    // char strbuf[1024];
    // sprintf(strbuf, "atk:%d, def:%d, max_hp:%d, cur_hp:%d, max_mp:%d, cur_mp:%d, speed:%d, critical_rate:%d, critical_damage:%d, evasion_rate:%d, hit_rate:%d", pf.atk(), pf.def(), pf.max_hp(), pf.cur_hp(), pf.max_mp(), pf.cur_mp(), pf.speed(), pf.critical_rate(), pf.critical_damage(), pf.evasion_rate(), pf.hit_rate());
    // std::string str_fight_info = "fight_info:" + std::string(strbuf);
    // PrintConsole(str_fight_info);
}

void RecvHelper::EnterDefaultSceneResponse(std::string& pbTy, std::string& buff){
    service::scene::RequestEnterDefaultScene_Response response;
    response.ParseFromString(buff);
    PrintConsole(response.Utf8DebugString());
}

void RecvHelper::SkillRet(std::string& pbTy, std::string& buff){
    service::scene::RequestUseSkill::Response response;
    response.ParseFromString(buff);
    PrintConsole(response.Utf8DebugString());
}

// void RecvHelper::EnterSceneRet(std::string& pbTy, std::string& buff){
//     scene::EnterSceneRet ret;
//     ret.ParseFromString(buff);
//     PrintConsole(ret.Utf8DebugString());

//     int map_type_id = ret.type_id();
//     int map_unique_id = ret.unique_id();

//     auto map_obj = GameMapMgr::GetInstance().AddNewMap(map_type_id, map_unique_id);
//     auto main_player = App::GetIns().GetMainPlayer();
//     for(auto& player: ret.players()){
//         LOG_DEBUG("AddPlayer:{}, {}, {}", player.id(), player.x(), player.y());
//         map_obj->AddPlayer(player.id(), player.x(), player.y());
//         if(main_player->GetPlayerID() == player.id()){
//             main_player->SetMapInfo(map_type_id, map_unique_id);
//             main_player->SetPos(player.x(), player.y());
//         }

//     }
//     PrintConsole(map_obj->ShowMapInfo(main_player->GetPlayerID()));
// }

// void RecvHelper::LeaveSceneRet(std::string& pbTy, std::string& buff){
//     scene::LeaveSceneRet ret;
//     ret.ParseFromString(buff);
//     PrintConsole(ret.Utf8DebugString());
//     auto main_player = App::GetIns().GetMainPlayer();
//     int map_type_id = main_player->GetCurrentMapTypeID();
//     int map_unique_id = main_player->GetCurrentMapUniqueID();
//     auto map = GameMapMgr::GetInstance().GetMap(map_type_id, map_unique_id);
//     map->RemovePlayer(ret.player_id());
// }

void RecvHelper::MoveRet(std::string& pbTy, std::string& buff){
    service::scene::RequestMove::Response ret;
    ret.ParseFromString(buff);
    PrintConsole(ret.Utf8DebugString());

    // auto scene_info = ret.scene_info();
    // auto main_player = App::GetIns().GetMainPlayer();
    // int map_type_id = main_player->GetCurrentMapTypeID();
    // int map_unique_id = main_player->GetCurrentMapUniqueID();

    // auto map_obj = GameMapMgr::GetInstance().GetMap(map_type_id, map_unique_id);
    // map_obj->AddPlayer(ret.uid(), scene_info.position().x(), scene_info.position().y());
    // if(main_player->GetPlayerID() == ret.uid()){
    //     main_player->SetPos(scene_info.position().x(), scene_info.position().y());
    //     // main_player->SetSpeed(player.speed(), player.speed());
    //     main_player->SetDirection(static_cast<EDirection>(scene_info.position().direction()));
    // }

    // PrintConsole(map_obj->ShowMapInfo(main_player->GetPlayerID()));
}